<?php

namespace App\Admin\Service\Base;

use App\Common\Exception\ApiMessageException;
use App\Common\Util\Str;
use Plugins\Dev\Admin\Service\DevAuxService;
use Plugins\Dev\Admin\Service\MakeCURDFiles\ModelProperty;
use Plugins\Dev\Admin\Service\MakeCURDService;
use Sc\Util\Tool;

/**
 * Class UpdateCURDService
 */
class UpdateCURDService
{
    const ADD_ROW = "  // +++++++新增++++++";
    const DEL_ROW = "  // -------删除------";

    private string $tableStudly;
    private array  $paths = [];
    private array  $fields;

    public function __construct(
        private string          $table,
        private readonly string $dir,
        private readonly string $plugins)
    {
        $this->table       = DevAuxService::getTable($this->table, false);
        $this->tableStudly = Str::studly($this->table);
        $this->fields      = json_decode(json_encode(DevAuxService::aop()->getFields($this->table)), true);

        $this->scan();
        $this->addFieldsCheck();
    }

    /**
     * @return void
     */
    private function addFieldsCheck(): void
    {
        preg_match_all('/^ *\* +@property +\w+ +\$(\w+) +.*[\r\n]+$/m', file_get_contents($this->paths['Model']), $match);
        $oldFields = $match[1];

        foreach ($this->fields as &$field) {
            $field['needAdd']    = !in_array($field['COLUMN_NAME'], $oldFields);
            $field['field']      = $field['COLUMN_NAME'];
            $field['field_type'] = $field['COLUMN_TYPE'];
            $field['comment']    = $field['COLUMN_COMMENT'];
        }
    }

    private function scan(): void
    {
        $check = $this->plugins ? MakeCURDService::BASE_PLUGINS_FILE_DIR : MakeCURDService::BASE_FILE_DIR;

        foreach ($check as $mode => $path) {
            $path    = strtr($path, [':plugins' => $this->plugins]);
            $replace = ltrim($this->dir . '/' . $this->tableStudly, '/');

            if ($mode === 'Enum' && is_dir($enumDir = dirname(sprintf($path, $replace)))) {
                Tool::dir($enumDir)->each(function (Tool\Dir\EachFile $file) {
                    if (str_starts_with($file->filename, $this->tableStudly . "Enum")){
                        $this->paths['Enum:' . strtr($file->filename, [
                            $this->tableStudly . "Enum" => '',
                            '.php' => ''
                        ])] = $file->filepath;
                    }
                });
                continue;
            }
            if ($mode != 'View') {
                $this->paths[$mode] = sprintf($path, $replace);
                continue;
            }
            $path = sprintf($path, $replace . "/%s");
            foreach (['lists', 'create', 'edit'] as $view){
                if (is_file(sprintf($path, $view))) {
                    $this->paths['View:' . $view] = sprintf($path, $view);
                }
            }
        }
    }

    private function modelCodeUpdate(): bool|string
    {
        $code = file_get_contents($this->paths['Model']);

        $this->modelPropertyUpdate($code);
        $this->modeCreateSqlUpdate($code);
        $this->modelFillableUpdate($code);

        return $code;
    }

    /**
     * @param array $old
     * @param array $new
     *
     * @return array
     */
    private function combinationDifference(array $old, array $new): array
    {
        $cdOld = $cdNew = [];
        foreach ($old as $index => $item) {
            $cdOld["cd_" . $index] = $item;
        }
        foreach ($new as $index => $item) {
            $cdNew["cd_" . $index] = $item;
        }
        $delItem = array_diff($cdOld, $cdNew);
        $addItem = array_diff($cdNew, $cdOld);

        $res = [];
        foreach (array_merge($cdOld, $cdNew) as $index => $item) {
            if (!isset($delItem[$index]) && !isset($addItem[$index])) {
                $res[] = $item;
                continue;
            }

            if (isset($delItem[$index])){
                $res[] = $this->delRow($delItem[$index]);
            }
            if (isset($addItem[$index])){
                $res[] = $this->addRow($addItem[$index]);
            }
        }

        return $res;
    }

    private function addRow(string $rowCode): string
    {
        return $rowCode . self::ADD_ROW;
    }

    private function delRow(string $rowCode): string
    {
        return "// " . $rowCode . self::DEL_ROW;
    }


    public function check(): string
    {
        return $this->modelCodeUpdate();
    }

    /**
     * @param string $code
     *
     * @return void
     */
    private function modelPropertyUpdate(string &$code): void
    {
        $newProperty = ModelProperty::getCommentByFields($this->fields);

        preg_match_all("/^ \* @property.*$/m", $code, $property);

        $property    = array_map('rtrim', current($property));
        $newProperty = array_map('rtrim', $newProperty);

        $enums = [];
        foreach ($newProperty as &$value) {
            $value = strtr($value, ['：' => ':']);
            if (!str_contains($value, ':')) {
                continue;
            }
            list($value, $options) = explode(":", $value);
            $options = strtr($options, ['，' => ',']);

            $enums[] = array_map(function ($v) {
                list($key, $value) = explode('=', $v);
                return compact('key', 'value');
            }, explode(',', $options));
        }

        $propertyRes = $this->combinationDifference($property, $newProperty);

        $code = preg_replace("/ \* @property.*@property[^\r\n]+/s", implode(PHP_EOL, $propertyRes), $code);
    }

    /**
     * @param string $code
     *
     * @return void
     */
    private function modeCreateSqlUpdate(string &$code): void
    {
        preg_match("/<<<SQL(.*)SQL/s", $code, $match);

        $oldCreateSql = explode("\n", strtr(trim($match[1]), ["\r\n" => "\n"]));
        $newCreateSql = explode("\n", DevAuxService::aop()->getCreateSql($this->table));

        $createSql = $this->combinationDifference($oldCreateSql, $newCreateSql);
        $code      = preg_replace("/<<<SQL(.*)SQL/s", sprintf('<<<SQL%s%s%1$sSQL', PHP_EOL, implode(PHP_EOL, $createSql)), $code);
    }

    /**
     * @param string $code
     *
     * @return void
     */
    private function modelFillableUpdate(string &$code): void
    {
        $fillable = sprintf('    protected array $fillable = ["%s"];',
            implode('", "', array_diff(array_column($this->fields, 'field'), ['id', 'create_time', 'update_time', 'delete_time'])));

        preg_match('/protected array \$fillable[^\r\n]+/', $code, $match);

        if (trim($fillable) != trim($match[0])) {
            $code = strtr($code, [
                $match[0] => $this->delRow($match[0])
                    . PHP_EOL
                    . $this->addRow($fillable)
            ]);
        }
    }

    /**
     * @param string $ModelCode
     *
     * @return void
     * @throws ApiMessageException
     */
    public function exec(string $ModelCode): void
    {
        if (!$ModelCode) {
            throw new ApiMessageException('代码为空，不能更新');
        }

        throw new ApiMessageException("功能作用不大，暂停开发..,请使用创建功能替代");
    }
}